﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Objects;
using System.Globalization;
using System.Linq;
using EstateNuke.Data.Properties;

namespace EstateNuke.Data
{
    public partial class Entrance
    {
        #region Fileds & Properties

        /// <summary>
        /// </summary>
        public string DisplayName
        {
            get
            {
                if (!BuildingReference.IsLoaded)
                {
                    BuildingReference.Load(MergeOption.PreserveChanges);
                }
                return string.Format("{0}-{1}{2}", Building.DisplayName, Name, IsDeleted ? " (已删除)" : "");
            }
        }

        /// <summary>
        /// </summary>
        public IList<Room> Rooms
        {
            get
            {
                return InternalRooms.Where(room => room.IsDeleted == false).ToList();
            }
        }

        /// <summary>
        /// </summary>
        public IList<Room> AllRooms
        {
            get
            {
                return InternalRooms.ToList();
            }
        }

        /// <summary>
        /// </summary>
        public IList<Property> Properties
        {
            get
            {
                return InternalProperties.Where(property => property.IsDeleted == false).ToList();
            }
        }

        /// <summary>
        /// </summary>
        public IList<Property> AllProperties
        {
            get
            {
                return InternalProperties.ToList();
            }
        }

        #endregion

        #region Add & Remove Room

        /// <summary>
        /// </summary>
        /// <returns></returns>
        public Room AddRoom(User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            var room = new Room
                           {
                               RowId = Guid.NewGuid(),
                               CreatedDate = DateTime.Now,
                           };
            InternalRooms.Add(room);
            room.IsDeleted = false;
            room.ModifiedDate = DateTime.Now;
            room.ModifiedByUser = modifiedUser;
            room.SaleArea = 0;
            room.UnitPrice = 0;
            room.TotalPrice = 0;
            room.RoomStateId = 1;
            return room;
        }

        /// <summary>
        /// </summary>
        /// <param name = "floor"></param>
        /// <param name = "modifiedUser"></param>
        /// <returns></returns>
        public Room AddRoom(Floor floor, User modifiedUser)
        {
            if (floor == null)
            {
                throw new ArgumentNullException("floor");
            }
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            var room = new Room
                           {
                               RowId = Guid.NewGuid(),
                               CreatedDate = DateTime.Now,
                           };
            InternalRooms.Add(room);
            room.IsDeleted = false;
            room.ModifiedDate = DateTime.Now;
            room.ModifiedByUser = modifiedUser;
            room.Floor = floor;
            room.SaleArea = 0;
            room.UnitPrice = 0;
            room.TotalPrice = 0;
            room.RoomStateId = 1;
            return room;
        }

        /// <summary>
        /// </summary>
        /// <param name = "floor"></param>
        /// <param name = "name"></param>
        /// <param name = "modifiedUser"></param>
        /// <returns></returns>
        public Room AddRoom(Floor floor, string name, User modifiedUser)
        {
            if (floor == null)
            {
                throw new ArgumentNullException("floor");
            }
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name");
            }
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            if (InternalRooms.Any(item => item.IsDeleted == false && item.Floor == floor && item.Name == name))
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.DuplicatedRoom, name));
            }
            var room = InternalRooms.FirstOrDefault(item => item.IsDeleted && item.Floor == floor && item.Name == name);
            if (room == null)
            {
                room = new Room
                           {
                               RowId = Guid.NewGuid(),
                               CreatedDate = DateTime.Now,
                           };
                InternalRooms.Add(room);
            }
            room.IsDeleted = false;
            room.ModifiedDate = DateTime.Now;
            room.ModifiedByUser = modifiedUser;
            room.Floor = floor;
            room.Name = name;
            room.SaleArea = 0;
            room.UnitPrice = 0;
            room.TotalPrice = 0;
            room.RoomStateId = 1;
            return room;
        }

        /// <summary>
        /// </summary>
        /// <param name = "floor"></param>
        /// <param name = "number"></param>
        /// <param name = "modifiedUser"></param>
        /// <returns></returns>
        public Room AddRoom(Floor floor, short number, User modifiedUser)
        {
            if (floor == null)
            {
                throw new ArgumentNullException("floor");
            }
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            if (InternalRooms.Any(item => item.IsDeleted == false && item.Floor == floor && item.Number == number))
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.DuplicatedRoom, number));
            }
            var room = InternalRooms.FirstOrDefault(item => item.IsDeleted && item.Floor == floor && item.Number == number);
            if (room == null)
            {
                room = new Room
                           {
                               RowId = Guid.NewGuid(),
                               CreatedDate = DateTime.Now,
                           };
                InternalRooms.Add(room);
            }
            room.IsDeleted = false;
            room.ModifiedDate = DateTime.Now;
            room.ModifiedByUser = modifiedUser;
            room.Floor = floor;
            room.Number = number;
            room.SaleArea = 0;
            room.UnitPrice = 0;
            room.TotalPrice = 0;
            room.RoomStateId = 1;
            return room;
        }

        /// <summary>
        /// </summary>
        /// <param name = "floor"></param>
        /// <param name = "name"></param>
        /// <param name = "number"></param>
        /// <param name = "modifiedUser"></param>
        /// <returns></returns>
        public Room AddRoom(Floor floor, string name, short number, User modifiedUser)
        {
            if (floor == null)
            {
                throw new ArgumentNullException("floor");
            }
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name");
            }
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            if (InternalRooms.Any(item => item.IsDeleted == false && item.Floor == floor && (item.Name == name || item.Number == number)))
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.DuplicatedRoom, string.Format("{0}({1})", name, number)));
            }
            var room = InternalRooms.FirstOrDefault(item => item.IsDeleted && item.Floor == floor && (item.Name == name || item.Number == number));
            if (room == null)
            {
                room = new Room
                           {
                               RowId = Guid.NewGuid(),
                               CreatedDate = DateTime.Now,
                           };
                InternalRooms.Add(room);
            }
            room.IsDeleted = false;
            room.ModifiedDate = DateTime.Now;
            room.ModifiedByUser = modifiedUser;
            room.Floor = floor;
            room.Name = name;
            room.Number = number;
            room.SaleArea = 0;
            room.UnitPrice = 0;
            room.TotalPrice = 0;
            room.RoomStateId = 1;
            return room;
        }

        /// <summary>
        /// </summary>
        /// <param name = "room"></param>
        /// <param name = "modifiedUser"></param>
        public void RemoveRoom(Room room, User modifiedUser)
        {
            if (room == null)
            {
                throw new ArgumentNullException("room");
            }
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            if (InternalRooms.Contains(room))
            {
                room.Delete(modifiedUser);
            }
        }

        #endregion

        #region Generate Rooms

        /// <summary>
        /// </summary>
        /// <param name = "modifiedUser"></param>
        public void GenerateRooms(User modifiedUser)
        {
            GenerateRooms(RoomsNumberPerFloor, modifiedUser);
        }

        /// <summary>
        /// </summary>
        /// <param name = "floor"></param>
        /// <param name = "modifiedUser"></param>
        public void GenerateRooms(Floor floor, User modifiedUser)
        {
            if (floor == null)
            {
                throw new ArgumentNullException("floor");
            }
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            if (!BuildingReference.IsLoaded)
            {
                BuildingReference.Load(MergeOption.PreserveChanges);
            }
            if (!floor.BuildingReference.IsLoaded)
            {
                floor.BuildingReference.Load(MergeOption.PreserveChanges);
            }
            if (Building != floor.Building)
            {
                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.NotSameBuilding, floor.Building.RowId, Building.RowId), "floor");
            }
            var rooms = InternalRooms.Where(room => room.Floor == floor && room.Number >= 0).ToList();
            for (short roomNumber = 1; roomNumber <= RoomsNumberPerFloor; roomNumber++)
            {
                var currentRoom = rooms.FirstOrDefault(room => room.Floor == floor && room.Number == roomNumber);
                if (currentRoom == null)
                {
                    AddRoom(floor, string.Format("{0}{1}", floor.Number, roomNumber.ToString("00")), roomNumber, modifiedUser);
                }
            }
        }

        /// <summary>
        /// </summary>
        /// <param name = "roomsNumberPerFloor"></param>
        /// <param name = "modifiedUser"></param>
        public void GenerateRooms(short roomsNumberPerFloor, User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            if (roomsNumberPerFloor < 0)
            {
                throw new ArgumentOutOfRangeException("roomsNumberPerFloor");
            }
            if (!BuildingReference.IsLoaded)
            {
                BuildingReference.Load(MergeOption.PreserveChanges);
            }
            foreach (var floor in Building.AllFloors)
            {
                var rooms = InternalRooms.Where(room => room.Floor == floor && room.Number >= 0).ToList();
                for (short roomNumber = 1; roomNumber <= roomsNumberPerFloor; roomNumber++)
                {
                    var currentRoom = rooms.FirstOrDefault(room => room.Floor == floor && room.Number == roomNumber);
                    if (currentRoom == null)
                    {
                        AddRoom(floor, string.Format("{0}{1}", floor.Number, roomNumber.ToString("00")), roomNumber, modifiedUser);
                    }
                }
            }
        }

        #endregion

        #region Update Rooms

        /// <summary>
        /// </summary>
        /// <param name = "saleArea"></param>
        /// <param name = "unitPrice"></param>
        /// <param name = "totalPrice"></param>
        /// <param name = "roomType"></param>
        /// <param name = "usage"></param>
        /// <param name = "deliveryMode"></param>
        /// <param name = "modifiedUser"></param>
        public void UpdateRooms(decimal? saleArea, decimal? unitPrice, decimal? totalPrice, RoomType roomType, Usage usage, DeliveryMode deliveryMode, User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            foreach (var room in AllRooms)
            {
                if (saleArea.HasValue)
                {
                    room.SaleArea = saleArea.Value;
                }
                if (unitPrice.HasValue)
                {
                    room.UnitPrice = unitPrice.Value;
                }
                if (totalPrice.HasValue)
                {
                    room.TotalPrice = totalPrice.Value;
                }
                if (roomType != null)
                {
                    room.RoomType = roomType;
                }
                if (usage != null)
                {
                    room.Usage = usage;
                }
                if (deliveryMode != null)
                {
                    room.DeliveryMode = deliveryMode;
                }
                ModifiedDate = DateTime.Now;
                ModifiedByUser = modifiedUser;
            }
        }

        #endregion

        #region Add & Remove Property

        /// <summary>
        /// </summary>
        /// <returns></returns>
        public Property AddProperty(User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            var property = new Property
                               {
                                   RowId = Guid.NewGuid(),
                                   CreatedDate = DateTime.Now,
                               };
            InternalProperties.Add(property);
            property.IsDeleted = false;
            property.ModifiedDate = DateTime.Now;
            property.ModifiedByUser = modifiedUser;
            property.LeaseArea = 0;
            property.UnitPricePerMonth = 0;
            property.TotalPricePerMonth = 0;
            property.PropertyStateId = 1;
            return property;
        }

        /// <summary>
        /// </summary>
        /// <param name = "floor"></param>
        /// <param name = "modifiedUser"></param>
        /// <returns></returns>
        public Property AddProperty(Floor floor, User modifiedUser)
        {
            if (floor == null)
            {
                throw new ArgumentNullException("floor");
            }
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            var property = new Property
                               {
                                   RowId = Guid.NewGuid(),
                                   CreatedDate = DateTime.Now,
                               };
            InternalProperties.Add(property);
            property.IsDeleted = false;
            property.ModifiedDate = DateTime.Now;
            property.ModifiedByUser = modifiedUser;
            property.Floor = floor;
            property.LeaseArea = 0;
            property.UnitPricePerMonth = 0;
            property.TotalPricePerMonth = 0;
            property.PropertyStateId = 1;
            return property;
        }

        /// <summary>
        /// </summary>
        /// <param name = "floor"></param>
        /// <param name = "name"></param>
        /// <param name = "modifiedUser"></param>
        /// <returns></returns>
        public Property AddProperty(Floor floor, string name, User modifiedUser)
        {
            if (floor == null)
            {
                throw new ArgumentNullException("floor");
            }
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name");
            }
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            if (InternalProperties.Any(item => item.IsDeleted == false && item.Floor == floor && item.Name == name))
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.DuplicatedProperty, name));
            }
            var property = InternalProperties.FirstOrDefault(item => item.IsDeleted && item.Floor == floor && item.Name == name);
            if (property == null)
            {
                property = new Property
                               {
                                   RowId = Guid.NewGuid(),
                                   CreatedDate = DateTime.Now,
                               };
                InternalProperties.Add(property);
            }
            property.IsDeleted = false;
            property.ModifiedDate = DateTime.Now;
            property.ModifiedByUser = modifiedUser;
            property.Floor = floor;
            property.Name = name;
            property.LeaseArea = 0;
            property.UnitPricePerMonth = 0;
            property.TotalPricePerMonth = 0;
            property.PropertyStateId = 1;
            return property;
        }

        /// <summary>
        /// </summary>
        /// <param name = "floor"></param>
        /// <param name = "number"></param>
        /// <param name = "modifiedUser"></param>
        /// <returns></returns>
        public Property AddProperty(Floor floor, short number, User modifiedUser)
        {
            if (floor == null)
            {
                throw new ArgumentNullException("floor");
            }
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            if (InternalProperties.Any(item => item.IsDeleted == false && item.Floor == floor && item.Number == number))
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.DuplicatedProperty, number));
            }
            var property = InternalProperties.FirstOrDefault(item => item.IsDeleted && item.Floor == floor && item.Number == number);
            if (property == null)
            {
                property = new Property
                               {
                                   RowId = Guid.NewGuid(),
                                   CreatedDate = DateTime.Now,
                               };
                InternalProperties.Add(property);
            }
            property.IsDeleted = false;
            property.ModifiedDate = DateTime.Now;
            property.ModifiedByUser = modifiedUser;
            property.Floor = floor;
            property.Number = number;
            property.LeaseArea = 0;
            property.UnitPricePerMonth = 0;
            property.TotalPricePerMonth = 0;
            property.PropertyStateId = 1;
            return property;
        }

        /// <summary>
        /// </summary>
        /// <param name = "floor"></param>
        /// <param name = "name"></param>
        /// <param name = "number"></param>
        /// <param name = "modifiedUser"></param>
        /// <returns></returns>
        public Property AddProperty(Floor floor, string name, short number, User modifiedUser)
        {
            if (floor == null)
            {
                throw new ArgumentNullException("floor");
            }
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name");
            }
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            if (InternalProperties.Any(item => item.IsDeleted == false && item.Floor == floor && (item.Name == name || item.Number == number)))
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.DuplicatedProperty, string.Format("{0}({1})", name, number)));
            }
            var property = InternalProperties.FirstOrDefault(item => item.IsDeleted && item.Floor == floor && (item.Name == name || item.Number == number));
            if (property == null)
            {
                property = new Property
                               {
                                   RowId = Guid.NewGuid(),
                                   CreatedDate = DateTime.Now,
                               };
                InternalProperties.Add(property);
            }
            property.IsDeleted = false;
            property.ModifiedDate = DateTime.Now;
            property.ModifiedByUser = modifiedUser;
            property.Floor = floor;
            property.Name = name;
            property.Number = number;
            property.LeaseArea = 0;
            property.UnitPricePerMonth = 0;
            property.TotalPricePerMonth = 0;
            property.PropertyStateId = 1;
            return property;
        }

        /// <summary>
        /// </summary>
        /// <param name = "property"></param>
        /// <param name = "modifiedUser"></param>
        public void RemoveProperty(Property property, User modifiedUser)
        {
            if (property == null)
            {
                throw new ArgumentNullException("property");
            }
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            if (InternalProperties.Contains(property))
            {
                property.Delete(modifiedUser);
            }
        }

        #endregion

        #region Generate Properties

        /// <summary>
        /// </summary>
        /// <param name = "modifiedUser"></param>
        public void GenerateProperties(User modifiedUser)
        {
            GenerateProperties(PropertiesNumberPerFloor, modifiedUser);
        }

        /// <summary>
        /// </summary>
        /// <param name = "floor"></param>
        /// <param name = "modifiedUser"></param>
        public void GenerateProperties(Floor floor, User modifiedUser)
        {
            if (floor == null)
            {
                throw new ArgumentNullException("floor");
            }
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            if (!BuildingReference.IsLoaded)
            {
                BuildingReference.Load(MergeOption.PreserveChanges);
            }
            if (!floor.BuildingReference.IsLoaded)
            {
                floor.BuildingReference.Load(MergeOption.PreserveChanges);
            }
            if (Building != floor.Building)
            {
                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.NotSameBuilding, floor.Building.RowId, Building.RowId), "floor");
            }
            var properties = InternalProperties.Where(property => property.Floor == floor && property.Number >= 0).ToList();
            for (short propertyNumber = 1; propertyNumber <= PropertiesNumberPerFloor; propertyNumber++)
            {
                var currentProperty = properties.FirstOrDefault(property => property.Floor == floor && property.Number == propertyNumber);
                if (currentProperty == null)
                {
                    AddProperty(floor, string.Format("{0}{1}", floor.Number, propertyNumber.ToString("00")), propertyNumber, modifiedUser);
                }
            }
        }

        /// <summary>
        /// </summary>
        /// <param name = "propertiesNumberPerFloor"></param>
        /// <param name = "modifiedUser"></param>
        public void GenerateProperties(short propertiesNumberPerFloor, User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            if (propertiesNumberPerFloor < 0)
            {
                throw new ArgumentOutOfRangeException("propertiesNumberPerFloor");
            }
            if (!BuildingReference.IsLoaded)
            {
                BuildingReference.Load(MergeOption.PreserveChanges);
            }
            foreach (var floor in Building.AllFloors)
            {
                var properties = InternalProperties.Where(property => property.Floor == floor && property.Number >= 0).ToList();
                for (short propertyNumber = 1; propertyNumber <= propertiesNumberPerFloor; propertyNumber++)
                {
                    var currentProperty = properties.FirstOrDefault(property => property.Floor == floor && property.Number == propertyNumber);
                    if (currentProperty == null)
                    {
                        AddProperty(floor, string.Format("{0}{1}", floor.Number, propertyNumber.ToString("00")), propertyNumber, modifiedUser);
                    }
                }
            }
        }

        #endregion

        #region Update Properties

        /// <summary>
        /// </summary>
        /// <param name = "leaseArea"></param>
        /// <param name = "unitPricePerMonth"></param>
        /// <param name = "totalPricePerMonth"></param>
        /// <param name = "usage"></param>
        /// <param name = "modifiedUser"></param>
        public void UpdateProperties(decimal? leaseArea, decimal? unitPricePerMonth, decimal? totalPricePerMonth, Usage usage, User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            foreach (var property in AllProperties)
            {
                if (leaseArea.HasValue)
                {
                    property.LeaseArea = leaseArea.Value;
                }
                if (unitPricePerMonth.HasValue)
                {
                    property.UnitPricePerMonth = unitPricePerMonth.Value;
                }
                if (totalPricePerMonth.HasValue)
                {
                    property.TotalPricePerMonth = totalPricePerMonth.Value;
                }
                if (usage != null)
                {
                    property.Usage = usage;
                }
                ModifiedDate = DateTime.Now;
                ModifiedByUser = modifiedUser;
            }
        }

        #endregion

        #region Ensure

        /// <summary>
        /// </summary>
        public void Ensure()
        {
            if (string.IsNullOrEmpty(Name))
            {
                throw new EntityException(string.Format(CultureInfo.CurrentCulture, Resources.NotNullAllowed, RowId, "Entrance", "Name"));
            }
            if (RoomsNumberPerFloor < 0)
            {
                throw new EntityException(string.Format(CultureInfo.CurrentCulture, Resources.NonNegativeNumberAllowed, RowId, "Entrance", "RoomsNumberPerFloor"));
            }
            if (PropertiesNumberPerFloor < 0)
            {
                throw new EntityException(string.Format(CultureInfo.CurrentCulture, Resources.NonNegativeNumberAllowed, RowId, "Entrance", "PropertiesNumberPerFloor"));
            }
            Name = Name.Length > 64 ? Name.Substring(0, 64) : Name;
        }

        #endregion

        #region Delete

        /// <summary>
        /// </summary>
        public void Delete(User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            IsDeleted = true;
            ModifiedDate = DateTime.Now;
            ModifiedByUser = modifiedUser;
            ((List<Room>) Rooms).ForEach(room => room.Delete(modifiedUser));
            ((List<Property>) Properties).ForEach(property => property.Delete(modifiedUser));
        }

        /// <summary>
        /// </summary>
        public void CancelDelete(User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            IsDeleted = false;
            ModifiedDate = DateTime.Now;
            ModifiedByUser = modifiedUser;
        }

        #endregion
    }
}